#pragma once

#include "../Core/BufferedSocket.h"
#include "../Core/SystemLogFile.h"
#include "../Core/Timer.h"
#include "TrackMap.h"

template <class T>
class TrackManager : public Timer, public TrackMap<T>
{
protected:
	InternetAddress m_LocalAddress;
	InternetAddress m_BroadcastAddress;
	InternetAddress mMulticastAddress;
	BufferedSocket m_Socket;
	std::queue<T*> m_QueueTrack;

	virtual void ExecuteByInterval()
	{
		while(!(m_QueueTrack.empty() && m_Socket.IsReceiverEmpty()))
		{
			if(!m_QueueTrack.empty())
			{
				T* t_T = m_QueueTrack.front();
				m_QueueTrack.pop();
				Update(t_T);
				continue;
			}
			if(!m_Socket.IsReceiverEmpty())
			{
				T* t_T = new T(this);
				Receive(t_T);
				Update(t_T);
			}
		}
	}

public:
	void* Parameter;

	TrackManager(bool p_OverWrite, InternetAddress p_LocalAddress, InternetAddress p_BroadcastAddress, void* p_Parameter = NULL, InternetAddress pMulticastAddress = InternetAddress("",0)) :
			TrackMap<T>(p_OverWrite),
			m_Socket(Socket(Protocol_Udp))
	{
		m_LocalAddress = p_LocalAddress;
		m_BroadcastAddress = p_BroadcastAddress;
		mMulticastAddress = pMulticastAddress;
		Parameter = p_Parameter;
		if(m_LocalAddress.PortNumber!=0)
		{
			m_Socket.Bind(m_LocalAddress);
			m_Socket.StartReceiver();
		}

        if (mMulticastAddress.Address != "") {
            m_Socket.JoinGroup(mMulticastAddress);
		}
	}

	virtual void Receive(T* p_T)
	{
		try
		{
			RawData t_RawData;
			InternetAddress t_InternetAddress;
			m_Socket.ReceiveFrom(t_RawData, t_InternetAddress);
			p_T->ReadRawData(t_RawData);
			SystemLogFile::GetSingleton().GetInputLogFile().WriteRawData(t_RawData, t_InternetAddress, m_LocalAddress);
		}
		catch(...)
		{
			Log::GetSingleton().Write(LogLevel_Error, "Error while receiving data");
		}
	}

	virtual void Send(T* p_T)
	{
		SendTo(p_T, m_BroadcastAddress);
	}

	virtual void SendTo(T* p_T, InternetAddress p_InternetAddress)
	{
		try
		{
			if(p_InternetAddress.PortNumber==0) throw std::exception();
			RawData t_RawData;
			p_T->WriteRawData(t_RawData);
			m_Socket.Socket::SendTo(t_RawData, p_InternetAddress);
			SystemLogFile::GetSingleton().GetOutputLogFile().WriteRawData(t_RawData, m_LocalAddress, p_InternetAddress);
		}
		catch(...)
		{
			Log::GetSingleton().Write(LogLevel_Error, "Error while sending data");
		}
	}

	virtual ~TrackManager()
	{
	}
};
